/**
 * \file Bottin.h
 * \brief Classe définissant un bottin téléphonique.
 * \author Pierre-Marc Levasseur, Simon Rodier
 * \version 0.1
 * \date juin 2014
 * 
 */

#ifndef BOTTIN__H
#define BOTTIN__H

#include <stdexcept>
#include <iostream>
#include <fstream> // pour les fichiers
#include <string>
#include <vector>

namespace TP2P2
{
	/** 
	* \class Bottin
	*
	* \brief classe représentant un bottin
	*
	*/
	class Bottin
	{
	public:
		/**
		 * \brief Constructeur par défaut de la classe. Permet de créer un bottin vide.
		 */
		Bottin(int size = 101);

		/**
		 * \brief constructeur à partir d'un fichier. Permet de charger le fichier fournit en argument et
		 * 	d'ajouter toutes les personnes décrites dans le fichier. Permet aussi de savoir combien de
		 * 	collisions se sont produites en ajoutant ces personnes.
		 *
		 * \pre le fichier est ouvert au préalable
		 * \post toutes les personnes dans le fichier seront proprement chargées
		 * \exception logic_error si le fichier n'est pas du bon format (ie: n'a pas le nombre d'entrée sur la première ligne)
		 * \exception invalid_argument si un numéro de téléphone est invalide
		 */
		Bottin(std::ifstream& file, int& nbCollisions);

		/**
		 * \brief Destructeur
		 * \post une instance de la classe sera détruite
		 */
		~Bottin();

		/**
		 * \brief méthode permettant de trouver une personne dans la table et renvoie un vector dont les éléments
		 * 		correspondent au numéro de téléphone, au numéro de fax et à l'adresse courriel de cette personne.
		 *
		 * \pre la personne est dans le bottin
		 * \post le bottin est inchangé et les éléments renvoyés correspondent au nom et prénom donné en argument
		 *
		 * \exception logic_error si la personne n'est pas dans le bottin
		 */
		std::vector<std::string> trouverAvecNomPrenom(const std::string& nom, const std::string& prenom) const;

		/**
		 * \brief méthode permettant de trouver une personne dans la table à partir de son numéro de téléphone et renvoie
		 * 	un vector de string dont les éléments correspondent au nom, prénom, numéro de fax et à l'adresse courriel de cette personne.
		 *
		 * \pre la personne ayant le numéro de téléphone passé en argument est dans le bottin
		 * \post le bottin est inchangé et les éléments renvoyées correspondent au numéro de téléphone donné en argument
		 *
		 * \exception 	logic_error si la personne n'est pas dans le bottin
		 * 				invalid_argument si le numéro de téléphone n'est pas du bon format
		 */
		std::vector<std::string> trouverAvecTelephone(const std::string& telephone) const;

		/**
		 * \brief méthode permettant de savoir si une des entrées du bottin correspond au nom et au prénom donné en argument
		 */
		bool contains(const std::string& nom, const std::string& prenom) const;
		/**
		 * \brief surcharge de la méthode contains qui permet de trouver si une des entrées correspond au numéro de téléphone donné en argument.
		 * \exception 	invalid_argument si le numéro de tel n'est pas dans le bon format
		 */
		bool contains(const std::string& telephone) const;

		/**
		 * \brief méthode permettant d'ajouter une personne au bottin
		 *
		 * \pre il y a assez de mémoire pour ajouter une personne
		 * \pre la personne à ajouter ne fait pas déjà partie du bottin
		 * \post la personne sera ajouté au bottin
		 *
		 * \exception bad_alloc s'il n'y a pas assez de mémoire
		 * \exception logic_error si la personne fait déjà partie du bottin
		 * \exception invalid_argument si le numéro de tel n'est pas dans le bon format
		 */
		void ajouterPersonne(const std::string& nom, const std::string& prenom, const std::string& telephone,
				const std::string& fax, const std::string& adresseCourriel, int& nbCollisions);

		/**
		 * \brief méthode permettant de vérifier si le bottin est vide.
		 */
		bool estVide() const;

		/**
		 * \brief méthode permettant de savoir combien de personne il y a dans le bottin.
		 */
		int size() const;

		void affiche() const;

	private:

		/**
		 * \enum EntryType
		 *
		 * \brief les différents états d'une entrée dans une table
		 */
		enum EntryType {
			ACTIVE, /*!< l'entrée est active */
			EMPTY, /*!< l'entrée est vide */
			DELETED /*!< l'entrée est supprimé */
		};
		/**
		 * \class Entree
		 *
		 * \brief classe représentant une entrée du bottin
		 *
		 */
		class Entree
		{
		public:

			std::string nom;
			std::string prenom;
			std::string tel;
			std::string fax;
			std::string courriel;

			Entree(const std::string& n, const std::string & p,  const std::string & t, const std::string & f, const std::string & c )
			: nom(n), prenom(p), tel(t), fax(f), courriel(c) { }

			/**
			 *  \brief Constructeur par défaut qui initialise une entrée à vide
			 */
			Entree() {} ;

		};

		/**
		 * \class EntreeHashTab
		 *
		 * \brief classe représentant une entrée dans une table de dispersion
		 */
		class EntreeHashTab
		{
		public:
			int position; /*!< position dans tabEntree */
			std::string tel;
			std::string nom;
			std::string prenom;
			EntryType etat; /*!< état de l'entrée dans la table de dispersion */

			/**
			 *  \brief constructeur par défaut qui crée des entrées vides (aucune position, etat empty)
			 */
			EntreeHashTab() : position(0), etat(EMPTY)
			{};

			EntreeHashTab(int position, const std::string& tel, const std::string& nom, const std::string& p, EntryType e)
			: position(position), tel(tel), nom(nom), prenom(p), etat(e) {}
		};

		std::vector<Entree> tabEntree; /*!< table des entrées */
		std::vector<EntreeHashTab*> tabNomPrenom; /*!< table de dispersion sur la clé Nom/Prénom */
		std::vector<EntreeHashTab*> tabTelephone; /*!< table de dispersion sur la clé téléphone */
		unsigned int cpt; /*!< nombre de personne dans la table des entrées */
		static const double TAUX_MAX = 50.0; /*!< taux de remplissage maximum dans la table, sinon, on double sa taille. */

		/**
		 * \brief méthode permettant d'ajouter une entrée au tableau des entrées et de placer sa position dans les deux tables de dispersions.
		 */
		void _ajouterEntree(Entree& personne, int& nbCollisions);

		/**
		 * \brief méthode de hachage permettant de trouver une position correspondant au nom et prénom fournit en argument
		 */
		int hashNomPrenom(const std::string& nom, const std::string& prenom, int& nbCollisions) const;

		/**
		 * \brief méthode de hachage permettant de trouver une position correspondant au numéro de téléphone fournit en argument
		 */
		int hashTelephone(const std::string& telephone, int& nbCollisions) const;

		/**
		 * \brief méthode permettant de redimensionner la table de dispersion nom et prénom si le taux de remplissage dépasse un certain seuil
		 */
		void rehashTabNomPrenom(int&);

		/**
		 * \brief méthode permettant de redimensionner la table de dispersion téléphone si le taux de remplissage dépasse un certain seuil
		 */
		void rehashTabTelephone(int&);

		/**
		 *  \brief vérifie qu'un numéro de téléphone est dans un format valide
		 */
		bool checkTelephone(const std::string& telephone) const;


		/**
		 *  \brief renvois un numéro de téléphone dans un format valide
		 *  \exception invalid_argument si le numéro de téléphone est invalide
		 */
		std::string convertTelephone(const std::string& telephone) const;

		/**
		 *  \brief Cherche le prochain nombre premier apr�s n
		 *
		 * On assume que n est > 0
		 */
		int nextPrime (int n);

		//
		/**
		 *  \brief V�rifie si n est premier
		 *
		 * On assume que n est > 0
		 */
		bool isPrime (int n);



	};

} // namespace TP2P2

#endif /* BOTTIN__H */
